/*
    Copyright 2020-2024. Huawei Technologies Co., Ltd. All rights reserved.

    Licensed under the Apache License, Version 2.0 (the "License")
    you may not use this file except in compliance with the License.
    You may obtain a copy of the License at

        https://www.apache.org/licenses/LICENSE-2.0

    Unless required by applicable law or agreed to in writing, software
    distributed under the License is distributed on an "AS IS" BASIS,
    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    See the License for the specific language governing permissions and
    limitations under the License.
*/

part of huawei_nearbyservice;

class TransferData {
  int? id;
  int? type;

  /// Used if [type] is [DataTypes.stream], otherwise null.
  final TransferDataStream? stream;

  /// Used if [type] is [DataTypes.file], otherwise null.
  final TransferDataFile? file;

  /// Used if [type] is [DataTypes.bytes], otherwise null.
  final Uint8List? bytes;

  /// Hash value of the data object that was created and transferred
  /// on the platform side. This will be accessible to Flutter with
  /// the callback that returns the transferred data,
  /// such as [HMSTransferEngine.dataOnTransferUpdate].
  ///
  /// NOTE: Since the [TransferData] objects that are passed along to the platform side
  /// are used to create instances of the Data class in the SDK, if you try to use the
  /// [hashCode] values generated by Dart, the values may differ with the hash codes
  /// of the Data objects generated on the platform side.
  int? hash;

  TransferData({
    this.id,
    this.type,
    this.bytes,
    this.stream,
    this.file,
    this.hash,
  });

  factory TransferData.fromMap(Map<dynamic, dynamic> map) {
    return TransferData(
      id: map['id'],
      type: map['type'],
      hash: map['hash'],
      bytes: map['bytes'],
      file: map['file'] != null ? TransferDataFile.fromMap(map['file']) : null,
      stream: map['stream'] != null
          ? TransferDataStream.fromMap(map['stream'])
          : null,
    );
  }

  factory TransferData.dataBytesFactory(Uint8List bytes, int? id) {
    return TransferData(
      id: id,
      type: DataTypes.bytes,
      bytes: bytes,
      file: null,
      stream: null,
    );
  }

  factory TransferData.dataFileFactory(TransferDataFile file, int? id) {
    return TransferData(
      id: id,
      type: DataTypes.file,
      bytes: null,
      file: file,
      stream: null,
    );
  }

  factory TransferData.dataStreamFactory(TransferDataStream stream, int? id) {
    return TransferData(
      id: id,
      type: DataTypes.stream,
      bytes: null,
      file: null,
      stream: stream,
    );
  }

  factory TransferData.fromBytes(Uint8List bytes) {
    return TransferData.dataBytesFactory(bytes, null);
  }

  factory TransferData.fromStream({
    String? url,
    Uint8List? content,
  }) {
    if (url == null && content == null) {
      throw ArgumentError.notNull('url & content');
    }
    return TransferData.dataStreamFactory(
      TransferDataStream(
        url: url,
        content: content,
      ),
      null,
    );
  }

  factory TransferData.fromFile(
    String? filePath, [
    int? size,
  ]) {
    if (filePath == null) {
      throw ArgumentError.notNull('filePath');
    }
    return TransferData.dataFileFactory(
      TransferDataFile(
        filePath: filePath,
        size: size,
      ),
      null,
    );
  }

  Map<String, dynamic> toMap() {
    return <String, dynamic>{
      'id': id,
      'type': type,
      'hash': hash,
      'bytes': bytes,
      'stream': stream?.toMap(),
      'file': file?.toMap(),
    };
  }
}

class TransferDataFile extends TransferData {
  String? filePath;
  int? size;

  TransferDataFile({
    required this.filePath,
    this.size,
  });

  factory TransferDataFile.fromMap(Map<dynamic, dynamic> map) {
    return TransferDataFile(
      filePath: map['filePath'],
      size: map['size'],
    );
  }

  @override
  Map<String, dynamic> toMap() {
    return <String, dynamic>{
      'filePath': filePath,
      'size': size,
    };
  }
}

class TransferDataStream extends TransferData {
  /// URL or URI for the stream to be transferred.
  ///
  /// NOTE: Only used when sending data.
  /// Meaning, For [TransferData] objects that are returned with
  /// [HMSTransferEngine.dataOnReceived] this will always be null.
  String? url;

  /// Java [InputStream] converted to a byte array. ([UInt8List])
  /// This is the data that will be transferred when
  /// [HMSTransferEngine.sendData] is called, if [url] is null.
  Uint8List? content;

  TransferDataStream({
    this.url,
    this.content,
  });

  factory TransferDataStream.fromMap(Map<dynamic, dynamic> map) {
    return TransferDataStream(
      url: map['url'],
      content: map['content'],
    );
  }

  @override
  Map<String, dynamic> toMap() {
    return <String, dynamic>{
      'url': url,
      'content': content,
    };
  }
}

class TransferStateUpdate {
  final int? status;
  final int? dataId;
  final int? totalBytes;
  final int? bytesTransferred;

  /// Hash value of the data object that was created and transferred
  /// on the platform side. This will be accessible to Flutter with
  /// the callback that returns the transferred data,
  /// such as [HMSTransferEngine.dataOnTransferUpdate].
  ///
  /// NOTE: Since the [TransferData] objects that are passed along to the platform side
  /// are used to create instances of the Data class in the SDK, if you try to use the
  /// [hashCode] values generated by Dart, the values may differ with the hash codes
  /// of the Data objects generated on the platform side.
  final int? hash;

  TransferStateUpdate({
    this.status,
    this.dataId,
    this.totalBytes,
    this.bytesTransferred,
    this.hash,
  });

  factory TransferStateUpdate.fromMap(Map<dynamic, dynamic> map) {
    return TransferStateUpdate(
      status: map['status'],
      dataId: map['dataId'],
      bytesTransferred: map['bytesTransferred'],
      totalBytes: map['totalBytes'],
      hash: map['hash'],
    );
  }

  Map<String, dynamic> toMap() {
    return <String, dynamic>{
      'status': status,
      'dataId': dataId,
      'bytesTransferred': bytesTransferred,
      'totalBytes': totalBytes,
      'hash': hash,
    };
  }

  bool equals(dynamic object) {
    return identical(this, object) ||
        (object is TransferStateUpdate &&
            status == object.status &&
            bytesTransferred == object.bytesTransferred &&
            totalBytes == object.totalBytes &&
            dataId == object.dataId &&
            hash == object.hash);
  }
}
